package xin.marcher.module.lifecycle.job;

import com.alibaba.fastjson.JSON;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import xin.marcher.module.common.constants.construction.ProductConstants;
import xin.marcher.module.common.core.PageResult;
import xin.marcher.module.common.enums.YesOrNoEnum;
import xin.marcher.module.common.enums.lifecycle.AssessmentTypeEnum;
import xin.marcher.module.common.enums.lifecycle.MarketExpriResultEnum;
import xin.marcher.module.common.enums.lifecycle.ProductStageEnum;
import xin.marcher.module.common.enums.lifecycle.ProductStatusEnum;
import xin.marcher.module.common.exception.BaseBizException;
import xin.marcher.module.common.exception.ProductErrorCodeEnum;
import xin.marcher.module.lifecycle.converter.ItemExpriResultConverter;
import xin.marcher.module.lifecycle.domain.dto.*;
import xin.marcher.module.lifecycle.domain.entity.ItemExpriResultBufferDO;
import xin.marcher.module.lifecycle.domain.entity.ItemExpriResultDO;
import xin.marcher.module.lifecycle.domain.request.ItemExpriResultBufferRequest;
import xin.marcher.module.lifecycle.domain.request.QueryPeriodListRequest;
import xin.marcher.module.lifecycle.job.context.XxlJobHelper;
import xin.marcher.module.lifecycle.mq.producer.DefaultProducer;
import xin.marcher.module.lifecycle.repository.CategoryExpriWhiteRepository;
import xin.marcher.module.lifecycle.repository.ItemExpriResultRepository;
import xin.marcher.module.lifecycle.repository.ItemPeriodStageRepository;
import xin.marcher.module.lifecycle.repository.ProductExpriRepository;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 生命周期阶段考核
 */
@Slf4j
@Component
public class CalculateExpriResultSchedule {

    /**
     * 考核结果资源管理
     */
    @Autowired
    private ItemExpriResultRepository itemExpriResultRepository;

    /**
     * 生命周期资源管理
     */
    @Autowired
    private ItemPeriodStageRepository itemPeriodStageRepository;

    /**
     * 考核白名单资源管理
     */
    @Autowired
    private CategoryExpriWhiteRepository categoryExpriWhiteRepository;

    /**
     * 商品考核资源管理
     */
    @Autowired
    private ProductExpriRepository productExpriRepository;

    /**
     * 考核结果对象克隆
     */
    @Resource
    private ItemExpriResultConverter itemExpriResultConverter;

    /**
     * MQ producer
     */
    @Autowired
    private DefaultProducer defaultProducer;

    /**
     * 定时任务：根据商品的质量和诚信问题、销量、客诉率等数据，对试销期、成熟期商品进行考核，并将考核结果写入考核结果缓冲表
     * 一般情况下：每天凌晨3点钟执行
     */
    @XxlJob("assessing")
    @Transactional(rollbackFor = Exception.class)
    public void assessing() {
        // 1.获取考核类型参数(试销期1，成熟期2)
        Integer assessmentType = getAssessmentType();
        // 2.查询出考核指标数据
        List<ProductExpriDataResultDTO> totalExpriDataList = productExpriRepository.listExpriDataResultByPage(assessmentType);
        // 3.考核指标数据对应的itemIdList
        List<String> itemIdList = getItemIdList(totalExpriDataList);
        // 4.考核指标数据对应的categoryIdList
        List<Integer> categoryIdList = getCategoryIdList(totalExpriDataList);
        // 5.查询生命周期阶段
        Map<String, ItemPeriodStageDTO> itemPeriodStageMap = listItemPeriodStageByPage(assessmentType, itemIdList);
        // 6.过滤掉商品所处生命周期阶段和当前考核类型不匹配的数据，并返回生命周期阶段Map
        totalExpriDataList = filterPeriodConsistencyToMap(totalExpriDataList, itemPeriodStageMap);
        // 7.过滤出在白名单中免考核的考核指标数据
        List<ProductExpriDataResultDTO> nonAssessmentExpriDataList = filterNonAssessmentToList(totalExpriDataList, categoryIdList);
        // 8.分页查询出考核配置数据
        Map<Integer, ProductExpriConfigurationDTO> expriConfigurationMap = listExpriConfigurationByPage(categoryIdList, assessmentType);
        // 9.批量查质量或诚信问题数据，并过滤出存在质量或诚信问题，考核不通过的数据
        List<ProductExpriDataResultDTO> assessFailureList = filterConformingIntegrityRecordToList(totalExpriDataList, itemIdList);
        // 10.比较销售额和销售率、客诉率等考核指标项，将考核数据分别划分到：考核不通过、考核通过、考核中三个集合中
        List<ProductExpriDataResultDTO> assessingList = filterSalesVolumePassTargetToList(totalExpriDataList,
                assessFailureList, expriConfigurationMap, assessmentType);
        // 11.将考核不通过、考核中、考核通过，免考核的考核指标数据，批量写入到考核结果缓冲表
        batchInsertAssessmentResult(itemIdList, assessFailureList, assessingList, totalExpriDataList, nonAssessmentExpriDataList, itemPeriodStageMap);
        // 12.将参与本轮考核计算的考核指标数据都设置为失效状态
        batchInvalidExpriData(itemIdList);
    }

    /**
     * 根据考核结果缓冲表中的考核状态和生命周期阶段，写入供需属性
     */
    @XxlJob("calculateExpri")
    @Transactional(rollbackFor = Exception.class)
    public void calculateExpri() {
        // 1.分页查，每页1000条数据
        // 2.批量判断是否考核通过。通过的放在passList，失败的放在failList
        // 3.针对failList的逻辑，执行对象转换，设置供需状态，批量保存到考核结果表
        // 4.针对passList的逻辑，执行对象转换，判断所处生命周期，拆分成滞销期list，试销期list
        // 5.针对滞销期/试销期list，执行供需状态设置，批量保存到考核结果表
        // 6.批量针对passList发送考核通过消息，针对failList发送考核失败消息
        // 7.批量逻辑删除所有的考核通过/不通过的buffer数据

        // 查询出考核结果数据
        List<ItemExpriResultBufferDO> passExpriResultBufferList = getPassExpriResultBufferList();
        List<ItemExpriResultBufferDO> failExpriResultBufferList = getFailExpriResultBufferList();

        log.info("passExpriResultBufferList:{}", JSON.toJSONString(passExpriResultBufferList));
        log.info("failExpriResultBufferList:{}", JSON.toJSONString(failExpriResultBufferList));
        // 批量处理考核成功结果
        List<ItemExpriResultDO> passedHandledList = batchHandlePassExpriResults(passExpriResultBufferList);
        // 批量处理考核失败结果
        List<ItemExpriResultDO> failedHandledList = batchHandleFailExpriResults(failExpriResultBufferList);

        log.info("passedHandledList:{}", JSON.toJSONString(passedHandledList));
        log.info("failedHandledList:{}", JSON.toJSONString(failedHandledList));

        // 批量分批次写入处理结果到考核表中
        batchSaveExpriResult(passedHandledList);
        batchSaveExpriResult(failedHandledList);

        // 批量更新考核结果buffer数据为已失效
        itemExpriResultRepository.batchDeleteExpriBuffer(passExpriResultBufferList);
        itemExpriResultRepository.batchDeleteExpriBuffer(failExpriResultBufferList);
    }

    /**
     * 考核失败结果处理供需属性
     *
     * @param failExpriResultBufferList
     */
    private List<ItemExpriResultDO> batchHandleFailExpriResults(List<ItemExpriResultBufferDO> failExpriResultBufferList) {
        // 考核结果数据集，考核缓冲数据处理转换成考核结果数据集合，并批量保存至数据库
        List<ItemExpriResultDO> failExpriResultList = new ArrayList<>(failExpriResultBufferList.size());
        List<String> itemIds = new ArrayList<>(failExpriResultBufferList.size());
        // 处理考核失败数据，设置淘汰期供需属性
        for (ItemExpriResultBufferDO resultBufferDO : failExpriResultBufferList) {
            // 克隆缓存对象属性到考核结果对象
            ItemExpriResultDO itemExpriResultDO = itemExpriResultConverter.converterBuffer(resultBufferDO);
            itemExpriResultDO.setUpdateUser(1);
            itemExpriResultDO.initCommon();
            // 设置淘汰期供需状态
            setPreEliminateSupplyStatus(itemExpriResultDO);
            failExpriResultList.add(itemExpriResultDO);
            // 提取itemId
            itemIds.add(itemExpriResultDO.getItemId());
        }
        Integer fromIndex = 0;
        while (true) {
            // 如果起始大于了list的结束位置，就退出循环
            if (fromIndex > failExpriResultBufferList.size() - 1) {
                break;
            }
            List<String> sbList = itemIds
                    .subList(fromIndex, Math.min(fromIndex + ProductConstants.QUERY_ITEM_MAX_COUNT,
                            failExpriResultBufferList.size()));
            // 分批次更新商品前一次考核结果数据为失效
            itemExpriResultRepository.batchDeleteExpriResult(sbList);
            fromIndex += ProductConstants.QUERY_ITEM_MAX_COUNT;
        }
        return failExpriResultList;
    }

    /**
     * 考核成功结果处理供需属性
     *
     * @param passExpriResultBufferList
     * @return
     */
    private List<ItemExpriResultDO> batchHandlePassExpriResults(List<ItemExpriResultBufferDO> passExpriResultBufferList) {
        // 批量处理考核成功结果
        List<String> itemIds = new ArrayList<>(passExpriResultBufferList.size());
        for (ItemExpriResultBufferDO resultBufferDO : passExpriResultBufferList) {
            // 商品itemId
            String itemId = resultBufferDO.getItemId();
            itemIds.add(itemId);
        }
        log.info("itemIds:{}", JSON.toJSONString(itemIds));
        // 分页处理考核通过结果
        List<ItemPeriodStageDTO> stageDTOS = new ArrayList<>(passExpriResultBufferList.size());
        Integer fromIndex = 0;
        while (true) {
            // 如果起始大于了list的结束位置，就退出循环
            if (fromIndex > passExpriResultBufferList.size() - 1) {
                break;
            }
            List<String> sbList = itemIds
                    .subList(fromIndex, Math.min(fromIndex + ProductConstants.QUERY_ITEM_MAX_COUNT,
                            passExpriResultBufferList.size()));
            // 分批次更新商品前一次考核结果数据为失效
            itemExpriResultRepository.batchDeleteExpriResult(sbList);
            // 分批查询商品生命周期所处阶段
            stageDTOS.addAll(itemPeriodStageRepository
                    .queryPeriodStageByItemIds(new QueryPeriodListRequest(sbList)));
            fromIndex += ProductConstants.QUERY_ITEM_MAX_COUNT;
        }
        log.info("stageDTOS:{}", JSON.toJSONString(stageDTOS));
        HashMap<String, Integer> itemStageMap = new HashMap(stageDTOS.size());

        // 转换成map结构，避免后续处理的时候，多次遍历stageDTOS list
        for (ItemPeriodStageDTO itemPeriodStageDTO : stageDTOS) {
            itemStageMap.put(itemPeriodStageDTO.getItemId(), itemPeriodStageDTO.getPeriodStage());
        }

        log.info("itemStageMap:{}", JSON.toJSONString(itemStageMap));

        // 循环设置考核结果供需属性
        List<ItemExpriResultDO> handledPassResults = new ArrayList<>(passExpriResultBufferList.size());
        for (ItemExpriResultBufferDO resultBufferDO : passExpriResultBufferList) {
            // 生命周期阶段
            Integer periodStage = itemStageMap.get(resultBufferDO.getItemId());
            ItemExpriResultDO itemExpriResultDO = itemExpriResultConverter.converterBuffer(resultBufferDO);
            // 试销期
            if (periodStage.equals(ProductStageEnum.TRY_SALE.getCode())) {
                // 设置对象初始值
                itemExpriResultDO.setUpdateUser(1);
                itemExpriResultDO.initCommon();
                // 设置试销期供需状态
                setTrySaleSupplyStatus(itemExpriResultDO);
                handledPassResults.add(itemExpriResultDO);
            }
            // 成熟期
            if (periodStage.equals(ProductStageEnum.GROWN.getCode())) {
                // 设置对象初始值
                itemExpriResultDO.setUpdateUser(1);
                itemExpriResultDO.initCommon();
                // 设置成熟期供需状态
                setGrownSupplyStatus(itemExpriResultDO);
                handledPassResults.add(itemExpriResultDO);
            }
        }
        return handledPassResults;
    }

    /**
     * 批量保存处理过的考核结果数据到考核结果
     *
     * @param handledList
     */
    private void batchSaveExpriResult(List<ItemExpriResultDO> handledList) {
        // 分页写入考核失败结果至考核结果表
        Integer fromIndex = 0;
        try {
            while (true) {
                // 判断下一次的list拆分是否超过了
                if (fromIndex > handledList.size() - 1) {
                    break;
                }
                // 每200条数据执行一次批量写
                List<ItemExpriResultDO> subList = handledList
                        .subList(fromIndex, Math.min(fromIndex + ProductConstants.QUERY_ITEM_MAX_COUNT,
                                handledList.size()));
                itemExpriResultRepository.saveItemExpriResultBatch(subList);
                // 设置下一页的起始位置
                fromIndex += ProductConstants.QUERY_ITEM_MAX_COUNT;
                Thread.sleep(20);
            }
        } catch (InterruptedException e) {
            throw new BaseBizException("批量保存考核结果至考核结果表异常");
        }
    }

    /**
     * 查询考核通过的buffer数据list
     *
     * @return
     */
    private List<ItemExpriResultBufferDO> getPassExpriResultBufferList() {
        List<ItemExpriResultBufferDO> passExpriResultBufferList = new ArrayList<>(ProductConstants.QUERY_ITEM_MAX_COUNT * 10);
        Integer pageNum = 1;
        try {
            while (true) {
                // 分页查询考核结果缓冲数据
                PageResult<ItemExpriResultBufferDO> page = itemExpriResultRepository
                        .queryPassExpriResultBufferByPage(pageNum, ProductConstants.QUERY_ITEM_MAX_COUNT * 5);
                // 内容为空时，跳出循环
                if (Objects.isNull(page) || CollectionUtils.isEmpty(page.getContent())) {
                    break;
                }
                // 查询到的结果集加入到通过list中
                passExpriResultBufferList.addAll(page.getContent());
                // 页码+1
                pageNum += 1;
                Thread.sleep(20);
            }
        } catch (InterruptedException e) {
            throw new BaseBizException("根据考核结果更新供需状态job，查询缓冲数据执行异常");
        }
        return passExpriResultBufferList;
    }

    /**
     * 查询考核失败的buffer数据list
     *
     * @return
     */
    private List<ItemExpriResultBufferDO> getFailExpriResultBufferList() {
        List<ItemExpriResultBufferDO> passExpriResultBufferList = new ArrayList<>(ProductConstants.QUERY_ITEM_MAX_COUNT * 10);
        Integer pageNum = 1;
        try {
            while (true) {
                // 分页查询考核结果缓冲数据
                PageResult<ItemExpriResultBufferDO> page = itemExpriResultRepository
                        .queryFailExpriResultBufferByPage(pageNum, ProductConstants.QUERY_ITEM_MAX_COUNT * 5);
                // 内容为空时，跳出循环
                if (Objects.isNull(page) || CollectionUtils.isEmpty(page.getContent())) {
                    break;
                }
                // 查询到的结果集加入到通过list中
                passExpriResultBufferList.addAll(page.getContent());
                // 页码+1
                pageNum += 1;
                Thread.sleep(20);
            }
        } catch (InterruptedException e) {
            throw new BaseBizException("根据考核结果更新供需状态job，查询缓冲数据执行异常");
        }
        return passExpriResultBufferList;
    }

    /**
     * 将参与本轮考核计算的考核指标数据都设置为失效状态
     *
     * @param itemIdList 本轮考核指标数据对应的itemId集合
     */
    private void batchInvalidExpriData(List<String> itemIdList) {
        productExpriRepository.invalidExpriDataResult(itemIdList);
    }

    /**
     * 分页查询生命周期阶段，返回Map
     *
     * @param assessmentType 考核类型
     * @param itemIdList     商品ID集合
     * @return 生命周期阶段集合，key为itemId
     */
    private Map<String, ItemPeriodStageDTO> listItemPeriodStageByPage(Integer assessmentType, List<String> itemIdList) {
        // 批量查询出生命周期对象
        QueryPeriodListRequest request = new QueryPeriodListRequest();
        request.setItemIdList(itemIdList);
        List<ItemPeriodStageDTO> itemPeriodStageList = itemPeriodStageRepository.queryPeriodStageByItemIds(request);

        // 过滤出生命周期阶段和考核类型匹配的生命周期数据
        Map<String, ItemPeriodStageDTO> itemPeriodStageMap = itemPeriodStageList.stream()
                // 过滤掉生命周期阶段和当前考核类型不匹配的数据
                .filter(itemPeriodStage -> assessmentType.equals(itemPeriodStage.getPeriodStage()))
                // 将过滤后的生命周期数据转换为Map，itemId作为Key
                .collect(Collectors.toMap(ItemPeriodStageDTO::getItemId, itemPeriodStage -> itemPeriodStage, (oldKey, newKey) -> oldKey));
        return itemPeriodStageMap;
    }

    /**
     * 批量将考核指标数据写入到考核结果缓冲表中
     *
     * @param itemIdList                 itemId集合
     * @param assessFailureList          考核不通过的考核指标数据
     * @param assessingList              考核中的考核指标数据
     * @param totalExpriDataList         考核通过的考核指标数据
     * @param nonAssessmentExpriDataList 免考核的考核指标数据
     * @param itemPeriodStageMap         生命周期数据集合，key为itemId
     */
    private void batchInsertAssessmentResult(List<String> itemIdList,
                                             List<ProductExpriDataResultDTO> assessFailureList,
                                             List<ProductExpriDataResultDTO> assessingList,
                                             List<ProductExpriDataResultDTO> totalExpriDataList,
                                             List<ProductExpriDataResultDTO> nonAssessmentExpriDataList,
                                             Map<String, ItemPeriodStageDTO> itemPeriodStageMap) {
        // 1.分页查询考核缓冲表中考核状态是考核中的数据
        Map<String, ItemExpriResultBufferDTO> itemExpriResultBufferMap = itemExpriResultRepository.listAssessingBufferResultByPage(itemIdList);

        // 2.批量写入考核不通过的考核指标数据
        batchInsertByMarketExpriType(itemExpriResultBufferMap, assessFailureList, itemPeriodStageMap,
                MarketExpriResultEnum.FAIL_PASS_EXAMINATION.getCode(), BigDecimal.ONE.intValue(), BigDecimal.ONE.intValue());
        // 3.批量写入考核中的考核指标数据
        batchInsertByMarketExpriType(itemExpriResultBufferMap, assessingList, itemPeriodStageMap,
                MarketExpriResultEnum.UNDER_EXAMINATION.getCode(), BigDecimal.ONE.intValue(), BigDecimal.ONE.intValue());
        // 4.批量写入考核通过的考核指标数据
        batchInsertByMarketExpriType(itemExpriResultBufferMap, totalExpriDataList, itemPeriodStageMap,
                MarketExpriResultEnum.PASS_EXAMINATION.getCode(), BigDecimal.ZERO.intValue(), BigDecimal.ZERO.intValue());
        // 5.批量写入免考核指标数据
        batchInsertByMarketExpriType(itemExpriResultBufferMap, nonAssessmentExpriDataList, itemPeriodStageMap,
                MarketExpriResultEnum.NON_EXAMINATION.getCode(), BigDecimal.ZERO.intValue(), BigDecimal.ZERO.intValue());
    }

    /**
     * 根据考核状态批量将考核指标数据批量写入到考核结果缓冲表
     * 考核不通过：  新增操作默认连续失败次数为1，修改操作连续失败次数增加1
     * 考核中：     新增操作默认连续失败次数为1，修改操作连续失败次数增加1
     * 考核通过：   新增操作默认连续失败次数为0，修改操作连续失败次数增加0
     * 免考核：     新增操作默认连续失败次数为0，修改操作连续失败次数增加0
     *
     * @param itemExpriResultBufferMap 已存在的考核状态为考核中的考核结果缓冲数据，key为itemId
     * @param expriDataResultList      考核指标数据集合
     * @param itemPeriodStageMap       生命周期数据，key为itemId
     * @param marketExpriResultType    考核状态：0：考核中；1：考核通过；2：考核不通过；3：不考核或暂停考核
     * @param defaultFailTimes         新增考核缓冲结果数据时，默认的失败次数
     * @param incrementFailTimes       修改考核缓冲结果数据时，增加的失败次数
     */
    private void batchInsertByMarketExpriType(Map<String, ItemExpriResultBufferDTO> itemExpriResultBufferMap,
                                              List<ProductExpriDataResultDTO> expriDataResultList,
                                              Map<String, ItemPeriodStageDTO> itemPeriodStageMap,
                                              Integer marketExpriResultType,
                                              Integer defaultFailTimes,
                                              Integer incrementFailTimes) {
        // 需要插入考核结果缓冲表的集合数据
        List<ItemExpriResultBufferRequest> insertRequest = new ArrayList<>();
        for (ProductExpriDataResultDTO result : expriDataResultList) {
            String itemId = result.getItemId();
            // 如果考核结果缓冲表中不存在该记录
            if (Objects.isNull(itemExpriResultBufferMap.get(itemId))) {
                // 准备批量插入数据
                ItemExpriResultBufferRequest request = new ItemExpriResultBufferRequest();
                request.setItemId(itemId);
                request.setCategoryId(result.getCategoryId());
                request.setPeriodStageId(itemPeriodStageMap.get(itemId).getProductPeriodId());
                request.setFailTimes(defaultFailTimes);
                request.setMarketExpriResult(marketExpriResultType);
                request.setDelFlag(YesOrNoEnum.YES.getCode());
                insertRequest.add(request);
            } else {
                // 考核结果表中存在该记录，执行修改操作
                ItemExpriResultBufferRequest request = new ItemExpriResultBufferRequest();
                request.setItemId(itemId);
                Integer failTimes = itemExpriResultBufferMap.get(itemId).getFailTimes() + incrementFailTimes;
                request.setFailTimes(failTimes);
                request.setMarketExpriResult(marketExpriResultType);
                itemExpriResultRepository.updateItemExpriResultBuffer(request);
            }
        }
        // 批量插入考核结果缓冲表
        itemExpriResultRepository.insertBatch(insertRequest);
    }

    /**
     * 获取考核类型参数
     *
     * @return
     */
    private Integer getAssessmentType() {
        // 考核类型
        String assessmentType = XxlJobHelper.getJobParam();
        // 是否传入考核类型参数
        return checkJobParams(assessmentType);
    }

    /**
     * 检查是否传入考核类型参数
     *
     * @param assessmentType 考核类型参数
     * @return 考核类型参数
     */
    private Integer checkJobParams(String assessmentType) {
        // 定时任务没有指定考核类型
        if (StringUtils.isEmpty(assessmentType)) {
            throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode());
        }
        Integer assessmentTypeNum = Integer.parseInt(assessmentType);
        if (!assessmentTypeNum.equals(AssessmentTypeEnum.TRY_SALE_ASSESS.getCode()) && !assessmentTypeNum.equals(AssessmentTypeEnum.GROWN_SALE_ASSESS.getCode())) {
            throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode());
        }
        return assessmentTypeNum;
    }

    /**
     * 比较考核结果数据和考核配置数据，判断考核结果数据是否通过考核
     * <p>
     * 销售额数据未达到要求，本轮考核不通过
     * <p>
     * 销售比率、客诉率、损耗率等5项指标，达到要求的指标数量未超过考核配置中通过考核的指标数量，本轮考核不通过
     * <p>
     * 对于试销期考核，本轮考核不通过，则考核状态即为不通过；
     * 但是对于滞销期考核，本轮考核不通过，还需要比较考核配置中的连续失败次数
     * 如果连续失败次数超过了配置中的数值，则考核不通过
     * 否则，对连续失败次数进行累加操作，考核状态为考核中
     *
     * @param totalExpriDataList    考核指标数据(所有的考核数据集合)
     * @param assessFailureList     考核未通过的考核指标数据
     * @param expriConfigurationMap 考核指标配置
     * @param assessmentType        考核类型
     * @return 考核中的考核指标数据
     */
    private List<ProductExpriDataResultDTO> filterSalesVolumePassTargetToList(List<ProductExpriDataResultDTO> totalExpriDataList,
                                                                              List<ProductExpriDataResultDTO> assessFailureList,
                                                                              Map<Integer, ProductExpriConfigurationDTO> expriConfigurationMap,
                                                                              Integer assessmentType) {
        // 考核中的考核指标数据集合
        List<ProductExpriDataResultDTO> assessingList = new ArrayList<>();
        // 批量查询出连续失败次数，作为Map，id是itemId
        Map<String, Integer> failTimesMap = itemExpriResultRepository.listFailTimesByPage();

        for (ProductExpriDataResultDTO expriResult : totalExpriDataList) {
            // 考核指标数据对应的考核配置
            ProductExpriConfigurationDTO expriConfiguration = expriConfigurationMap.get(expriResult.getCategoryId());
            // 是否低于最低销售额
            if (expriResult.getSalesVolume() < expriConfiguration.getSalesVolume()) {
                fillListByAssessmentType(assessFailureList, assessingList, failTimesMap, assessmentType, expriResult, expriConfiguration);
            }
            // 销售比率、客诉率、退货率是否符合要求
            if (!checkAssessment(expriResult, expriConfiguration)) {
                fillListByAssessmentType(assessFailureList, assessingList, failTimesMap, assessmentType, expriResult, expriConfiguration);
            }
        }
        // 剔除掉考核不通过的考核指标数据
        totalExpriDataList.removeAll(assessFailureList);
        // 剔除掉考核中的考核指标数据
        totalExpriDataList.removeAll(assessingList);
        // 考核的流程走到这里，totalExpriDataList中剩下的就是考核成功的考核指标数据

        // 返回考核中的考核指标数据
        return assessingList;
    }

    /**
     * 考核不通过时，根据考核类型（试销期/滞销期）将考核指标数据，拆分填充到考核失败集合、考核中集合
     *
     * @param assessFailureList  考核不通过集合
     * @param assessingList      考核中集合
     * @param failTimesMap       连续失败次数数据Map，key是itemId
     * @param assessmentType     考核类型
     * @param expriResult        考核指标数据
     * @param expriConfiguration 考核指标配置
     */
    private void fillListByAssessmentType(List<ProductExpriDataResultDTO> assessFailureList,
                                          List<ProductExpriDataResultDTO> assessingList,
                                          Map<String, Integer> failTimesMap,
                                          Integer assessmentType,
                                          ProductExpriDataResultDTO expriResult,
                                          ProductExpriConfigurationDTO expriConfiguration) {
        // 如果是试销考核
        if (assessmentType.equals(AssessmentTypeEnum.TRY_SALE_ASSESS.getCode())) {
            // 直接添加到考核不通过集合中
            assessFailureList.add(expriResult);
        }
        // 如果是滞销考核
        if (assessmentType.equals(AssessmentTypeEnum.GROWN_SALE_ASSESS.getCode())) {
            // 比较失败次数
            Integer failTimes = failTimesMap.getOrDefault(expriResult.getItemId(), BigDecimal.ZERO.intValue());
            Integer failTimesConfig = expriConfiguration.getFailTimes();
            // 如果小于配置的连续失败次数
            if (++failTimes < failTimesConfig) {
                // 添加到考核中集合
                assessingList.add(expriResult);
            } else {
                // 否则添加到考核不通过集合
                assessFailureList.add(expriResult);
            }
        }
    }

    /**
     * 批量查询出质量或诚信问题数据集合，并对比考核数据，将对应的考核数据添加到考核不通过的集合中
     *
     * @param totalExpriDataList 考核结果数据
     * @param itemIdList         商品ID集合
     * @return 考核不通过考核指标数据集合
     */
    private List<ProductExpriDataResultDTO> filterConformingIntegrityRecordToList(List<ProductExpriDataResultDTO> totalExpriDataList, List<String> itemIdList) {
        // 批量查询出质量诚信问题数据集合
        List<ProductBadIssuesResultDTO> productBadIssuesResultList = productExpriRepository.queryProductBadIssuesResultByItemIdList(itemIdList);
        // List转为Map，itemId作为key
        Map<String, ProductBadIssuesResultDTO> productBadIssuesResultMap = productBadIssuesResultList.stream()
                .collect(Collectors.toMap(ProductBadIssuesResultDTO::getItemId,
                        productBadIssuesResult -> productBadIssuesResult, (oldKey, newKey) -> oldKey));
        // 过滤出存在质量问题的考核指标数据
        List<ProductExpriDataResultDTO> assessFailureList = totalExpriDataList.stream().filter(productExpriDataResultDTO -> {
            // 根据考核结果数据的itemId获取质量、诚信问题记录
            ProductBadIssuesResultDTO productBadIssuesResult = productBadIssuesResultMap.get(productExpriDataResultDTO.getItemId());
            // 如果该考核结果数据存在质量诚信问题，就保留，否则就过滤掉
            if (productBadIssuesResult == null) {
                return false;
            }
            if (productBadIssuesResult.getContractIntegrity().equals(YesOrNoEnum.YES.getCode()) &&
                    productBadIssuesResult.getNonConformingProduct().equals(YesOrNoEnum.YES.getCode())) {
                return false;
            }
            return true;
        }).collect(Collectors.toList());
        // 剔除掉在存在质量问题的考核指标数据
        totalExpriDataList.removeAll(assessFailureList);
        // 返回考核不通过的考核指标数据
        return assessFailureList;
    }

    /**
     * 分页查考核配置数据
     *
     * @param categoryIdList 品类ID集合
     * @return 封装为Map，categoryId作为key
     */
    private Map<Integer, ProductExpriConfigurationDTO> listExpriConfigurationByPage(List<Integer> categoryIdList, Integer assessmentType) {
        // 分页查询出考核配置数据
        List<ProductExpriConfigurationDTO> productExpriConfigurationList = productExpriRepository.queryExpriConfigurationByCategoryIdList(categoryIdList, assessmentType);
        // List转为Map，categoryId作为key
        Map<Integer, ProductExpriConfigurationDTO> productExpriConfigurationMap = productExpriConfigurationList.stream()
                .collect(Collectors.toMap(ProductExpriConfigurationDTO::getCategoryId,
                        productExpriConfiguration -> productExpriConfiguration, (oldKey, newKey) -> oldKey));
        return productExpriConfigurationMap;
    }

    /**
     * 过滤出在白名单中的考核指标数据
     *
     * @param totalExpriDataList 考核指标数据集合
     * @return 在白名单中的考核指标数据集合
     */
    private List<ProductExpriDataResultDTO> filterNonAssessmentToList(List<ProductExpriDataResultDTO> totalExpriDataList, List<Integer> categoryIdList) {
        // 1.分页查询出考核指标数据中对应的白名单所属的categoryIdList
        List<Integer> whiteCategoryIdList = categoryExpriWhiteRepository.queryExpriWhiteByCategoryIdList(categoryIdList);
        // 2.过滤出在白名单范围内的考核指标数据
        List<ProductExpriDataResultDTO> nonAssessmentExpriDataList = totalExpriDataList.stream()
                .filter(productExpriDataResultDTO -> whiteCategoryIdList.contains(productExpriDataResultDTO.getCategoryId()))
                .collect(Collectors.toList());
        // 3.剔除掉在白名单范围内的考核指标数据
        totalExpriDataList.removeAll(nonAssessmentExpriDataList);
        // 4.返回在白名单内的考核指标数据
        return nonAssessmentExpriDataList;
    }

    /**
     * 提取categoryIdList
     *
     * @param totalExpriDataList 考核指标数据集合
     * @return
     */
    private List<Integer> getCategoryIdList(List<ProductExpriDataResultDTO> totalExpriDataList) {
        return totalExpriDataList.stream().map(ProductExpriDataResultDTO::getCategoryId).distinct().collect(Collectors.toList());
    }

    /**
     * 提取itemIdList
     *
     * @param totalExpriDataList 考核指标数据集合
     * @return
     */
    private List<String> getItemIdList(List<ProductExpriDataResultDTO> totalExpriDataList) {
        return totalExpriDataList.stream().map(ProductExpriDataResultDTO::getItemId).collect(Collectors.toList());
    }

    /**
     * 过滤掉所处的生命周期阶段和当前考核类型不匹配的数据
     *
     * @param totalExpriDataList 考核数据集合
     * @param itemPeriodStageMap 生命周期Map，key为itemId
     * @return 生命周期阶段和考核类型匹配的考核指标数据集合
     */
    private List<ProductExpriDataResultDTO> filterPeriodConsistencyToMap(List<ProductExpriDataResultDTO> totalExpriDataList,
                                                                         Map<String, ItemPeriodStageDTO> itemPeriodStageMap) {
        // 过滤考核指标数据
        totalExpriDataList = totalExpriDataList.stream().
                filter(productExpriDataResultDTO -> Objects.nonNull(itemPeriodStageMap.get(productExpriDataResultDTO.getItemId())))
                .collect(Collectors.toList());
        return totalExpriDataList;
    }

    /**
     * 检查客诉率、退货率的指标是否符合要求
     *
     * @param expriData          考核指标数据
     * @param expriConfiguration 考核指标配置
     * @return true：符合；false：不符合
     */
    private boolean checkAssessment(ProductExpriDataResultDTO expriData, ProductExpriConfigurationDTO expriConfiguration) {
        // 考核通过指标数量
        Integer passTargetConfig = expriConfiguration.getPassTarget();
        Integer passTarget = 0;

        // 销售比例配置
        Integer soldPropotionConfig = expriConfiguration.getSoldPropotion();
        Integer soldPropotion = expriData.getSoldPropotion();
        if (soldPropotion >= soldPropotionConfig) {
            passTarget++;
        }

        // 每单利润率配置
        Integer orderProfitMarginConfig = expriConfiguration.getOrderProfitMargin();
        Integer orderProfitMargin = expriData.getOrderProfitMargin();
        if (orderProfitMargin >= orderProfitMarginConfig) {
            passTarget++;
        }

        // 退货比率配置
        Integer returnRateConfig = expriConfiguration.getReturnRate();
        Integer returnRate = expriData.getReturnRate();
        if (returnRate <= returnRateConfig) {
            passTarget++;
        }

        // 客诉率配置
        Integer complaintRateConfig = expriConfiguration.getComplaintRate();
        Integer complaintRate = expriData.getComplaintRate();
        if (complaintRate <= complaintRateConfig) {
            passTarget++;
        }

        // 损耗比率配置
        Integer lossRateConfig = expriConfiguration.getLossRate();
        Integer lossRate = expriData.getLossRate();
        if (lossRate <= lossRateConfig) {
            passTarget++;
        }

        if (passTarget >= passTargetConfig) {
            return true;
        }
        return false;
    }

    /**
     * 设置淘汰期供需状态
     *
     * @param itemExpriResultDO
     */
    private void setPreEliminateSupplyStatus(ItemExpriResultDO itemExpriResultDO) {
        itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.NO.getCode());
        itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.NO.getCode());
        itemExpriResultDO.setSaleStatus(YesOrNoEnum.YES.getCode());
    }

    /**
     * 设置试销期供需状态
     *
     * @param itemExpriResultDO
     */
    private void setTrySaleSupplyStatus(ItemExpriResultDO itemExpriResultDO) {
        itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.YES.getCode());
        itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.YES.getCode());
        itemExpriResultDO.setSaleStatus(YesOrNoEnum.YES.getCode());
    }

    /**
     * 设置成熟期供需状态
     *
     * @param itemExpriResultDO
     */
    private void setGrownSupplyStatus(ItemExpriResultDO itemExpriResultDO) {
        String itemId = itemExpriResultDO.getItemId();
        // 商品状态
        Integer itemStatus = itemPeriodStageRepository.getItemStatus(itemId);

        // 处于上架状态
        if (itemStatus.equals(ProductStatusEnum.PUT_ON_THE_MARKET.getCode())) {
            itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.YES.getCode());
            itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.YES.getCode());
            itemExpriResultDO.setSaleStatus(YesOrNoEnum.YES.getCode());
            return;
        }

        // 不处于上架状态
        itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.NO.getCode());
        itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.NO.getCode());
        itemExpriResultDO.setSaleStatus(YesOrNoEnum.NO.getCode());
    }
}
